package org.jeecg.pms.service.impl;

import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.shiro.SecurityUtils;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.modules.online.config.exception.BusinessException;
import org.jeecg.modules.system.service.ISysDictService;
import org.jeecg.modules.system.service.impl.SnRuleServiceImpl;
import org.jeecg.pms.entity.PmsProject;
import org.jeecg.pms.entity.PmsTask;
import org.jeecg.pms.entity.PmsTaskTime;
import org.jeecg.pms.mapper.PmsTaskTimeMapper;
import org.jeecg.pms.service.IPmsProjectService;
import org.jeecg.pms.service.IPmsTaskService;
import org.jeecg.pms.service.IPmsTaskTimeService;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;

@Service
public class PmsTaskTimeService extends ServiceImpl<PmsTaskTimeMapper, PmsTaskTime> implements IPmsTaskTimeService {
    // 任务状态：未开始
    private static final String TASK_UNSTATRT = "0";
    // 任务状态：进行中
    private static final String TASK_RUN = "1";
    // 任务状态：待审核
    private static final String TASK_CHECK = "2";
    // 任务状态：已完成
    private static final String TASK_END = "3";
    // 项目状态：执行中
    private static final String PROJECT_RUN = "2";
    // 项目类型：通用
    private static final String COMMON_TASK_TYPE = "common";
    private static final String PMS_COMMON_TASK_TYPE = "pms_common_task_type";


    @Autowired
    private IPmsTaskService pmsTaskService;
    @Autowired
    private IPmsProjectService pmsProjectService;
    @Autowired
    private SnRuleServiceImpl snRuleService;
    @Autowired
    private ISysDictService sysDictService;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void addWorkHours(PmsTaskTime pmsTaskTime) throws BusinessException {
        // 判断是否是专案还是通用日报
        if (COMMON_TASK_TYPE.equals(pmsTaskTime.getTaskType())) {
            PmsTask pmsTask = new PmsTask();
            pmsTask.setTaskType(COMMON_TASK_TYPE);
            pmsTask.setTaskName(sysDictService.queryDictTextByKey(PMS_COMMON_TASK_TYPE,pmsTaskTime.getTaskCode()));
            pmsTask.setTaskCode(snRuleService.execute(pmsTaskTime.getTaskCode() + "-${YYYY}${MM}${DD}-####",null).toString());
            pmsTaskTime.setTaskCode(pmsTask.getTaskCode());
            pmsTask.setProjectCode(COMMON_TASK_TYPE);
            pmsTask.setParentTaskCode("0");
            Date now = new Date();
            pmsTask.setStartTime(DateUtil.beginOfDay(now));
            pmsTask.setEndTime(DateUtil.offsetSecond(DateUtil.endOfDay(now), -1));
            pmsTask.setActualStartTime(pmsTask.getStartTime());
            pmsTask.setActualEndTime(pmsTaskTime.getStartTime());
            pmsTask.setTaskProgress(new BigDecimal(100));
            LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
            pmsTask.setTaskDirector(sysUser.getUsername());
            pmsTask.setPlanManHours(pmsTaskTime.getManHours());
            pmsTask.setActualManHours(pmsTaskTime.getManHours());
            pmsTask.setTaskStatus(TASK_END);
            pmsTaskService.save(pmsTask);

        }else {
            String projectCode = pmsTaskTime.getProjectCode();
            // 判断项目状态是否为执行中
            projectRunOrNot(projectCode);
            // 先获取任务状态，如果是未开始状态，需要更改状态为进行中
            PmsTask pmsTask = startTaskAndAddWorkHours(pmsTaskTime);

            pmsTaskService.updateById(pmsTask);
        }


        // 存储工时记录
        this.saveOrUpdate(pmsTaskTime);
    }

    /**
     * 增加task工时，如果首次填写任务，则开启任务为执行中（非持久化）
     * @param pmsTaskTime
     * @return
     * @throws BusinessException
     */
    @NotNull
    private PmsTask startTaskAndAddWorkHours(PmsTaskTime pmsTaskTime) throws BusinessException {
        LambdaQueryWrapper<PmsTask> pmsTaskLambdaQueryWrapper = new LambdaQueryWrapper<>();
        pmsTaskLambdaQueryWrapper.eq(PmsTask::getTaskCode, pmsTaskTime.getTaskCode());
        pmsTaskLambdaQueryWrapper.eq(PmsTask::getProjectCode, pmsTaskTime.getProjectCode());
        List<PmsTask> pmsTasks = pmsTaskService.list(pmsTaskLambdaQueryWrapper);
        if(CollectionUtils.isEmpty(pmsTasks)) {
            throw new BusinessException("未找到对应数据");
        }
        PmsTask pmsTask = pmsTasks.get(0);
        // BigDecimal alreadyHours = pmsTask.getActualManHours() == null?BigDecimal.ZERO: pmsTask.getActualManHours();
        if (TASK_UNSTATRT.equals(pmsTask.getTaskStatus())) {
            pmsTask.setTaskStatus(TASK_RUN);
            pmsTask.setActualStartTime(pmsTaskTime.getStartTime());
        }
        // update 2021-12-31 先不管这些逻辑判断，统一先覆盖task完成比
        pmsTask.setTaskProgress(pmsTaskTime.getTaskProgress());
//        // 判断完成比是否比task记录中的多，如果多，则不更新（仅限今日之前的taskTime）
//        // （防止几天前的进度更新产生数据错乱问题，比如今天进度是70%，改了昨天进度为80%，此时更新昨天的进度时不应该覆盖task中的）
//        // 今日任务可以修改为小于当前task中的任务完成比的，因为有可能是手抖填错了要改正回来
//        // （例如：未填写任务时task中是70%，结果填写错误是90%，其实是80%，此时是要允许将90%更改为80%的）
//        if (pmsTask.getTaskProgress() != null
//                && !DateUtil.isSameDay(pmsTaskTime.getStartTime(), new Date())
//                && pmsTask.getTaskProgress().compareTo(pmsTaskTime.getTaskProgress()) < 0) {
//            throw new BusinessException("更新完成度请勿大于现有完成度");
//        }
//        // 今日的不论大小都可以更新完成比
//        if (pmsTask.getTaskProgress() != null
//                && DateUtil.isSameDay(pmsTaskTime.getStartTime(), new Date())) {
//            pmsTask.setTaskProgress(pmsTaskTime.getTaskProgress());
//        }

        // 将此次工时计入实际工时中
        // update:2021-12-20 不再冗余此字段信息，依然按照实时查询taskTime数据计算得出
//        pmsTask.setActualManHours(alreadyHours.add(pmsTaskTime.getManHours()));

        // 判断进度，如果>=100,则更改为待审核
        if (pmsTask.getTaskProgress().compareTo(new BigDecimal(100)) > -1) {
            pmsTask.setTaskStatus(TASK_CHECK);
            // 如果没有审核人，则直接为已完成
            if (StringUtils.isEmpty(pmsTask.getTaskReviewer())) {
                pmsTask.setTaskStatus(TASK_END);
            }
        }
        // 判断是否为主任务，如果没有子任务，就直接更新项目进度即可，如果是子任务，则需要更新父任务进度
        PmsTask level1Task = updateProgress(pmsTask);


        // 更新项目完成度
        LambdaQueryWrapper<PmsProject> projectLambdaQueryWrapper = new QueryWrapper<PmsProject>().lambda().eq(PmsProject::getProjectCode, pmsTask.getProjectCode());
        PmsProject project = pmsProjectService.getOne(projectLambdaQueryWrapper);
        if (project != null && level1Task != null) {
            // 1.获取工时占比
            BigDecimal hoursPercent = level1Task.getPlanManHours().divide(project.getPlanManHours(), 4, BigDecimal.ROUND_HALF_UP);
            // 2.获取更新后的父任务进度占比
            BigDecimal progressPercent = hoursPercent.multiply(level1Task.getTaskProgress());
            // 3.对比原有进度占比，获得差值
            // 4.拿父任务进度加差值，即为更新后的进度
            BigDecimal currentProgress = project.getProjectProgress()==null?new BigDecimal("0"):project.getProjectProgress();
            BigDecimal parentProgress = currentProgress.add(
                    progressPercent.subtract(
                            level1Task.getParentProgressPart() == null ? new BigDecimal("0") : level1Task.getParentProgressPart()));
            project.setProjectProgress(parentProgress.compareTo(new BigDecimal("100"))>-1?new BigDecimal("100"):parentProgress);
            if (parentProgress.compareTo(new BigDecimal("100")) > -1) {
                project.setProjectStatus("3");
            }
            level1Task.setParentProgressPart(progressPercent);
            pmsProjectService.updateById(project);
            pmsTaskService.updateById(level1Task);
        }
        return pmsTask;
    }

    /**
     * 升级父任务进度
     * @param pmsTask
     * @return
     */
    private PmsTask updateProgress(PmsTask pmsTask) {
        // 判断是否为主任务，如果没有子任务，就直接更新项目进度即可，如果是子任务，则需要更新父任务进度
        if (!"0".equals(pmsTask.getParentTaskCode())) {

            // 先更新当前父任务的完成度，判断是否已经完成100%，如果已经完成，则更新父任务状态
            // 递归父任务
            LambdaQueryWrapper<PmsTask> queryWrapper = new QueryWrapper<PmsTask>().lambda().eq(PmsTask::getTaskCode, pmsTask.getParentTaskCode());
            queryWrapper.eq(PmsTask::getProjectCode, pmsTask.getProjectCode());
            PmsTask parentTask = pmsTaskService.getOne(queryWrapper);
            if (parentTask != null) {
                // 更新父任务的完成比
                // 完成比是以各子任务的工时占比来决定父任务中的完成比的占比的，
                // 所以应该拿当前子任务的预计工时/父任务的工时得到占比，然后以占比*当前子任务的完成比，得到此任务在父任务的完成比部分，再加上其他任务的
                // 1.获取工时占比
                BigDecimal hoursPercent = pmsTask.getPlanManHours().divide(parentTask.getPlanManHours(), 4, BigDecimal.ROUND_HALF_UP);
                // 2.获取更新后的父任务进度占比
                BigDecimal progressPercent = hoursPercent.multiply(pmsTask.getTaskProgress());
                // 3.对比原有进度占比，获得差值
                // 4.拿父任务进度加差值，即为更新后的进度

                BigDecimal currentProgress = parentTask.getTaskProgress()==null?new BigDecimal("0"):parentTask.getTaskProgress();
                BigDecimal parentProgress = currentProgress.add(
                        progressPercent.subtract(
                                pmsTask.getParentProgressPart() == null ? new BigDecimal("0") : pmsTask.getParentProgressPart()));

                parentTask.setTaskProgress(parentProgress);
                if (parentTask.getTaskProgress().compareTo(new BigDecimal(100)) > -1) {
                    parentTask.setTaskStatus(TASK_CHECK);
                    // 如果没有审核人，则直接为已完成
                    if (StringUtils.isEmpty(parentTask.getTaskReviewer())) {
                        parentTask.setTaskStatus(TASK_END);
                    }
                }
                pmsTask.setParentProgressPart(progressPercent);
                //  持久化父任务
                pmsTaskService.updateById(parentTask);
                // 先将pmstask持久化吧，后续优化（应该判断是否是中间层的pmstask,这些才应该持久化）
                pmsTaskService.updateById(pmsTask);
                return updateProgress(parentTask);
            }
        }
        return pmsTask;
    }

    /**
     * 判断项目是否为执行中，非此状态直接抛出业务异常
     * @param projectCode
     * @throws BusinessException
     */
    private void projectRunOrNot(String projectCode) throws BusinessException {
        LambdaQueryWrapper<PmsProject> pmsProjectLambdaQueryWrapper = new LambdaQueryWrapper<>();
        pmsProjectLambdaQueryWrapper.eq(PmsProject::getProjectCode, projectCode);
        List<PmsProject> pmsProjects = pmsProjectService.list(pmsProjectLambdaQueryWrapper);
        if (CollectionUtils.isEmpty(pmsProjects)) {
            throw new BusinessException("未找到对应项目数据");
        }
        if (!PROJECT_RUN.equals(pmsProjects.get(0).getProjectStatus())) {
            throw new BusinessException("项目状态非执行中，不允许执行");
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void addWorkHoursBatch(List<PmsTaskTime> pmsTaskTimeList) throws BusinessException {
        // 定义map存储pm_code,减少因判断项目状态而查询sql的次数
        Map<String, String> pmStatusMap = new HashMap<>();
        // 定义即将持久化的task容器
        List<PmsTask> tasks = new ArrayList<>();
        for (int i = 0; i < pmsTaskTimeList.size(); i++) {
            if (pmsTaskTimeList.get(0).getTaskType() != null) {

            }
            // 如果不存在map中，则查询pm状态，并将结果put进去
            if (!pmStatusMap.containsKey(pmsTaskTimeList.get(i).getProjectCode())) {
                projectRunOrNot(pmsTaskTimeList.get(i).getProjectCode());
                pmStatusMap.put(pmsTaskTimeList.get(i).getProjectCode(),null);
            }
            // 存在，则代表肯定可以进行任务的
            // 获取任务状态
            tasks.add(startTaskAndAddWorkHours(pmsTaskTimeList.get(i)));
        }
        // 业务处理完毕，批量update和save
        pmsTaskService.updateBatchById(tasks);
        this.saveOrUpdateBatch(pmsTaskTimeList);
    }
}
